﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
//using System.Drawing;

namespace CrayonsBox
{
    class ColorCanvas: Canvas
    {
        private static WriteableBitmap colorBitmap;
        private Color selectedColor = new Color();
        private ColorHSL selectedColorHSL = new ColorHSL();
        private Rect colorBitmapBound = new Rect(10, 10, 240, 241);
        private Rect lumBitmapBound = new Rect(270, 10, 20, 241);
        public ColorCanvas()
        {
            Margin = new Thickness(0.0, 0.0, 0, 0);
            Width = 240 + 20;
            Height = 241 + 20 + 80;
            if (colorBitmap == null)
            {
                colorBitmap = new WriteableBitmap((int)colorBitmapBound.Width, (int)colorBitmapBound.Height, 96, 96, PixelFormats.Rgb24, null);
                byte[] pixels = new byte[240 * 3];
                for (int j = 0; j < (int)colorBitmap.Height; j++)
                {
                    for (int i = 0; i < (int)colorBitmap.Width; i++)
                    {
                        int R = 0;
                        int G = 0;
                        int B = 0;
                        int Hue = i;
                        double Sat = ((double)(240 - j)) / 240.0;
                        int Lum = 120;

                        ColorRGB rgb = ColorRGB.FromHSL(((float)i) / 360f, Sat, 0.5);
                        pixels[3 * i] = rgb.R;
                        pixels[3 * i + 1] = rgb.G;
                        pixels[3 * i + 2] = rgb.B;
                    }
                    colorBitmap.WritePixels(new Int32Rect(0, j, 240, 1), pixels, 3 * 240, 0);
                }
            }
            Point colorPos = GetColorPos();
            GetColorByMouse((int)colorPos.X, (int)colorPos.Y);
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            drawingContext.DrawImage(colorBitmap, new Rect(colorBitmapBound.X, colorBitmapBound.Y, colorBitmap.Width, colorBitmap.Height));
            Pen p = new Pen(Brushes.Black, 1.0);
            Point colorPos = GetColorPos();
            drawingContext.DrawLine(p, new Point(Math.Max(colorPos.X - 4, colorBitmapBound.X), colorPos.Y)
                , new Point(Math.Min(colorPos.X + 4, colorBitmapBound.X + colorBitmapBound.Width), colorPos.Y));
            drawingContext.DrawLine(p, new Point(colorPos.X, Math.Max(colorPos.Y - 4, colorBitmapBound.Y))
                , new Point(colorPos.X, Math.Min(colorPos.Y + 4, colorBitmapBound.Y + colorBitmapBound.Height)));
            //drawingContext.DrawImage(lumBitmap, new Rect(lumBitmapBound.X, lumBitmapBound.Y, lumBitmap.Width, lumBitmap.Height));
            ColorHSL colorHSLTemp = new ColorHSL();
            colorHSLTemp.H = selectedColorHSL.H;
            colorHSLTemp.S = selectedColorHSL.S;
            colorHSLTemp.L = 120;
            Color colorMid = ColorRGB.FromHSL(colorHSLTemp);
            GradientStopCollection gradientStopCollection = new GradientStopCollection
                                     {
                                       new GradientStop(Color.FromArgb(255, 255, 255, 255), 0.0),
                                       new GradientStop(colorMid, 0.5),
                                       new GradientStop(Color.FromArgb(255, 0, 0, 0), 1.0)
                                     };

            Brush gradienBrush = new LinearGradientBrush(gradientStopCollection);
            drawingContext.DrawRectangle(gradienBrush, null, new Rect(lumBitmapBound.X, lumBitmapBound.Y, lumBitmapBound.Width, lumBitmapBound.Height));
            Point start = GetLumPos();
            LineSegment[] segments = new LineSegment[] { new LineSegment(new Point(start.X + 10, start.Y - 10), true), new LineSegment(new Point(start.X + 10, start.Y + 10), true) };
            PathFigure figure = new PathFigure(start, segments, true);
            PathGeometry geo = new PathGeometry(new PathFigure[] { figure });
            drawingContext.DrawGeometry(Brushes.Red, null, geo);

            Pen arrowBorder = new Pen(Brushes.Black, 2);
            drawingContext.DrawLine(arrowBorder, start, new Point(start.X + 10, start.Y - 10));
            drawingContext.DrawLine(arrowBorder, new Point(start.X + 10, start.Y - 10), new Point(start.X + 10, start.Y + 10));
            drawingContext.DrawLine(arrowBorder, new Point(start.X + 10, start.Y + 10), start);  

            Brush b = new SolidColorBrush(selectedColor);
            
            drawingContext.DrawRectangle(b, p, new Rect(10.0, colorBitmap.Height + 20.0, colorBitmap.Width, 40.0));
        }
        protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            GetColorByMouse((int)e.GetPosition(this).X, (int)e.GetPosition(this).Y);
            InvalidateVisual();
        }
        protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
            {
                GetColorByMouse((int)e.GetPosition(this).X, (int)e.GetPosition(this).Y);
                InvalidateVisual();
            }
        }
        public ColorHSL GetColorByMouse(int x, int y)
        {
            if (x >= 0 && x < colorBitmapBound.X + colorBitmapBound.Width + 10
                && y >= 0 && y < colorBitmapBound.Y + colorBitmapBound.Height + 10)
            {
                x -= 10;
                y -= 10;
                if (x < 0) x = 0;
                if (y < 0) y = 0;
                if (x >= 240) x = 239;
                if (y >= 241) y = 240;
                selectedColorHSL.H = (byte)x;
                selectedColorHSL.S = (byte)y;
                //UpdateLumBitmap(selectedColorHSL);
                selectedColor = ColorRGB.FromHSL(selectedColorHSL);
                return selectedColorHSL;
            }
            else if (x >= colorBitmapBound.X + colorBitmapBound.Width + 10 && x < lumBitmapBound.X + lumBitmapBound.Width + 10
                && y >= 0 && y < lumBitmapBound.Y + lumBitmapBound.Height + 10)
            {
                y -= 10;
                if (y < 0) y = 0;
                if (y >= 241) y = 240;
                selectedColorHSL.L = (byte)(240 - y);
                selectedColor = ColorRGB.FromHSL(selectedColorHSL);
                return selectedColorHSL;
            }
            return selectedColorHSL;
        }
        public Point GetColorPos()
        {
            Point p = new Point();
            p.X = colorBitmapBound.X + selectedColorHSL.H;
            p.Y = colorBitmapBound.Y + selectedColorHSL.S;
            return p;
        }
        public Point GetLumPos()
        {
            Point p = new Point();
            p.X = lumBitmapBound.X + lumBitmapBound.Width;
            p.Y = lumBitmapBound.Y + 240 - selectedColorHSL.L;
            return p;
        }
    }
    public class ColorHSL
    {
        public byte H;
        public byte S;
        public byte L;
    }
    public class ColorRGB
    {
        public byte R;
        public byte G;
        public byte B;
        public byte A;

        public ColorRGB()
        {
            R = 255;
            G = 255;
            B = 255;
            A = 255;
        }

        public ColorRGB(System.Drawing.Color value)
        {
            this.R = value.R;
            this.G = value.G;
            this.B = value.B;
            this.A = value.A;
        }
        public static implicit operator System.Drawing.Color(ColorRGB rgb)
        {
            System.Drawing.Color c = System.Drawing.Color.FromArgb(rgb.A, rgb.R, rgb.G, rgb.B);
            return c;
        }
        public static explicit operator ColorRGB(System.Drawing.Color c)
        {
            return new ColorRGB(c);
        }

        public static Color FromHSL(ColorHSL cHSL)
        {
            ColorRGB cRGB = FromHSLA((double)(cHSL.H) / 360.0, ((double)(240 - cHSL.S)) / 240.0, (double)(cHSL.L) / 240.0, 1.0);
            Color c = new Color();
            c.R = cRGB.R;
            c.G = cRGB.G;
            c.B = cRGB.B;
            c.A = 255;
            return c;
        }
        // Given H,S,L in range of 0-1
        // Returns a Color (RGB struct) in range of 0-255
        public static ColorRGB FromHSL(double H, double S, double L)
        {
            return FromHSLA(H, S, L, 1.0);
        }

        // Given H,S,L,A in range of 0-1
        // Returns a Color (RGB struct) in range of 0-255
        public static ColorRGB FromHSLA(double H, double S, double L, double A)
        {
            double v;
            double r, g, b;
            if (A > 1.0)
                A = 1.0;

            r = L;   // default to gray
            g = L;
            b = L;
            v = (L <= 0.5) ? (L * (1.0 + S)) : (L + S - L * S);
            if (v > 0)
            {
                double m;
                double sv;
                int sextant;
                double fract, vsf, mid1, mid2;

                m = L + L - v;
                sv = (v - m) / v;
                H *= 6.0;
                sextant = (int)H;
                fract = H - sextant;
                vsf = v * sv * fract;
                mid1 = m + vsf;
                mid2 = v - vsf;
                switch (sextant)
                {
                    case 0:
                        r = v;
                        g = mid1;
                        b = m;
                        break;
                    case 1:
                        r = mid2;
                        g = v;
                        b = m;
                        break;
                    case 2:
                        r = m;
                        g = v;
                        b = mid1;
                        break;
                    case 3:
                        r = m;
                        g = mid2;
                        b = v;
                        break;
                    case 4:
                        r = mid1;
                        g = m;
                        b = v;
                        break;
                    case 5:
                        r = v;
                        g = m;
                        b = mid2;
                        break;
                }
            }
            ColorRGB rgb = new ColorRGB();
            rgb.R = Convert.ToByte(r * 255.0f);
            rgb.G = Convert.ToByte(g * 255.0f);
            rgb.B = Convert.ToByte(b * 255.0f);
            rgb.A = Convert.ToByte(A * 255.0f);
            return rgb;
        }

        // Hue in range from 0.0 to 1.0
        public float H
        {
            get
            {
                // Use System.Drawing.Color.GetHue, but divide by 360.0F 
                // because System.Drawing.Color returns hue in degrees (0 - 360)
                // rather than a number between 0 and 1.
                return ((System.Drawing.Color)this).GetHue() / 360.0F;
            }
        }

        // Saturation in range 0.0 - 1.0
        public float S
        {
            get
            {
                return ((System.Drawing.Color)this).GetSaturation();
            }
        }

        // Lightness in range 0.0 - 1.0
        public float L
        {
            get
            {
                return ((System.Drawing.Color)this).GetBrightness();
            }
        }
    }
}
